﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FgoData.Core.Entity;
using FgoData.Core.Message.LeaderBoard;
using FgoData.Crawler.AppMedia;
using FgoData.Crawler.FgoWiki;
using FgoData.Crawler.Utility;
using Newtonsoft.Json;
using NPOI.Util;

namespace FgoData.Launcher
{
    using LeaderBoardMessage = FgoData.Core.Message.LeaderBoard.LeaderBoard;
    using LeaderBoardEntity = FgoData.Core.Entity.LeaderBoard;
    
    public class FgoDataHelper
    {
        protected FgowikiCrawler FgowikiCrawler { get; set; }
        protected AppMediaCrawler AppMediaCrawler { get; set; }
        
        public IDictionary<int, ServantInfo> Servants { get; set; }
        public IDictionary<int, IList<int>> AppMediaDict { get; set; }
        public LeaderBoardEntity LeaderBoardSsr { get; set; }
        public LeaderBoardEntity LeaderBoardSr { get; set; }

        public FgoDataHelper()
        {
            FgowikiCrawler = new FgowikiCrawler();
            AppMediaCrawler = new AppMediaCrawler();
        }

        public FgoDataHelper(FgowikiCrawler fgowikiCrawler, AppMediaCrawler appMediaCrawler)
        {
            FgowikiCrawler = fgowikiCrawler;
            AppMediaCrawler = appMediaCrawler;
        }
        
        public void LoadDataFromFgowiki(int maxId = 208)
        {
            Servants = new Dictionary<int, ServantInfo>();
            
            var list = FgowikiCrawler.FetchServants(1, maxId);
            foreach (var servant in list)
            {
                Servants[servant.Id] = servant;
            }
        }

        public void ApplyErrata()
        {
            var list = ExcelHelper.ReadAllErrata("App_Data/errata.xlsx");
            AppMediaDict = new Dictionary<int, IList<int>>();

            foreach (var errata in list)
            {
                ServantInfo si = Servants[errata.Id];
                
                if (!string.IsNullOrEmpty(errata.ChineseName))
                    si.ChineseName = errata.ChineseName;
                si.AppMediaId = errata.AppMediaId;
                si.MainAvatar = errata.AvatarUrl;
                
                Servants[errata.Id] = si;

                if (errata.AppMediaId != 0)
                {
                    if (!AppMediaDict.ContainsKey(errata.AppMediaId))
                    {
                        AppMediaDict[errata.AppMediaId] = new List<int>();
                    }
                    AppMediaDict[errata.AppMediaId].Add(errata.Id);
                }
            }
        }

        public void ExportFgoDataAsExcel(string path)
        {
            ExcelHelper.SaveFgoData(path, Servants.Values.ToList());
        }

        public void LoadLeaderBoardFromAppmedia()
        {
            LeaderBoardSsr = AppMediaCrawler.FetchLeaderBoard(5);
            LeaderBoardSr = AppMediaCrawler.FetchLeaderBoard(4);
        }

        public void ExportLeaderBoardAsJson()
        {
            var leaderBoardMessageSsr = GenerateLeaderBoardMessage(LeaderBoardSsr);
            var leaderBoardMessageSr = GenerateLeaderBoardMessage(LeaderBoardSr);
            
            string jsonSsr = JsonConvert.SerializeObject(leaderBoardMessageSsr);
            string jsonSr = JsonConvert.SerializeObject(leaderBoardMessageSr);
            
            File.WriteAllText(@"D:\temp\ssr_20180521.json", jsonSsr);
            File.WriteAllText(@"D:\temp\sr_20180521.json", jsonSr);
        }
        
        private LeaderBoardMessage GenerateLeaderBoardMessage(LeaderBoardEntity entity)
        {
            LeaderBoardMessage lboard = new LeaderBoardMessage();

            string rare = "";
            switch (entity.Rare)
            {
                case 5:
                    rare = "五";
                    break;
                case 4:
                    rare = "四";
                    break;
                case 3:
                    rare = "三";
                    break;
                case 2:
                    rare = "二";
                    break;
                case 1:
                    rare = "一";
                    break;
                case 0:
                    rare = "无";
                    break;
            }

            string updateTime = DateTime.Now.ToString("yyyy-MM-dd");
            lboard.Title = string.Format("Appmedia最强{0}星排行榜  {1}", rare, updateTime);
            lboard.Ranks = new List<Rank>();
            var ranks = from mark in entity.Marks group mark by mark.Rank;

            foreach (var rank in ranks)
            {
                Rank rankMsg = new Rank();

                string desc = "";
                if (entity.Rare == 5)
                {
                    switch (rank.Key)
                    {
                        case "SS":
                            desc = "环境";
                            break;
                        case "S+":
                            desc = "最上位";
                            break;
                        case "S":
                            desc = "上位";
                            break;
                        case "A":
                            desc = "中坚";
                            break;
                        case "B":
                            desc = "下位";
                            break;
                        case "C":
                            desc = "希望强化";
                            break;
                        default:
                            desc = "其他";
                            break;
                    }
                }
                rankMsg.Name = string.Format("▼{0}级别▼{1}", rank.Key, desc);

                rankMsg.Groups = new List<Group>();
                var smgroups = from sm in rank.ToList() group sm by sm.Score;
                foreach (var smgroup in smgroups)
                {
                    Group groupMsg = new Group();

                    var score = smgroup.Key;
                    groupMsg.Score = score;

                    groupMsg.Servants = new List<Servant>();
                    foreach (var servant in smgroup)
                    {
                        if (!AppMediaDict.ContainsKey(servant.AppMediaId)) continue;
                        
                        var ids = AppMediaDict[servant.AppMediaId];
                        foreach (var id in ids)
                        {
                            if (!Servants.ContainsKey(id)) continue;
                            var si = Servants[id];

                            Servant servantMsg = new Servant();
                            servantMsg.Id = id;
                            servantMsg.Name = servant.Name;
                            servantMsg.AvatarUrl = si.MainAvatar;

                            groupMsg.Servants.Add(servantMsg);
                        }
                    }

                    rankMsg.Groups.Add(groupMsg);
                }

                lboard.Ranks.Add(rankMsg);
            }

            // 排序，填色
            int index = 0;
            int amount = lboard.GroupCount;
            foreach (var rank in lboard.Ranks)
            {
                foreach (var group in rank.Groups)
                {
                    string color = GetColor(index++, amount);
                    group.Color = color;
                }
            }

            return lboard;
        }

        private static string GetColor(int index, int amount)
        {
            double p = index*1.0/(amount-1);
            int r=0, g=0, b=0;

            if (p<=0.25)
            {
                r = 0xff;
                b = 0;
                g = (int)(0xff * ((p - 0) / 0.25));
            }
            else if (p>0.25 && p<=0.5)
            {
                g = 0xff;
                b = 0;
                r = (int)(0xff * (1-((p - 0.25) / 0.25)));
            }
            else if (p > 0.5 && p <= 0.75)
            {
                r = 0;
                g = 0xff;
                b = (int)(0xff * ((p - 0.5) / 0.25));
            }
            else
            {
                r = 0;
                b = 0xff;
                g = (int)(0xff * (1-((p - 0.75) / 0.25)));
            }

            int rgb = (r << 16) | (g << 8) | b;
            return string.Format("#{0:X6}", rgb);
        }
    }
}